﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

using ProjectBase.Core;
using ProjectBase.Core.Tools.Export;

namespace ProjectBase.Tools.Export.Excel
{
    public class ExcelExporter : IExcelExporter
    {
        public bool Execute(IExcelExporterData model)
        {
            Model = model;

            return Setup()
                && ProcessDocument()
                && FlushDocument();
        }

        #region protected
        protected virtual bool Setup()
        {
            if (Model.Entities.IsEmpty())
            {
                this.PublishError(Constants.Messages.NullValueWasProvided1
                    , "Entities");
                return false;
            }

            try
            {
                Stream = new MemoryStream();
                return true;
            }
            catch (Exception ex)
            {
                this.PublishFatal(ex);
            }
            return false;
        }
        protected virtual bool ProcessDocument()
        {
            try
            {
                using (var package = SpreadsheetDocument.Create(Stream
                                   , SpreadsheetDocumentType.Workbook, true))
                {
                    FillPackage(package);
                }
                return true;
            }
            catch (Exception ex)
            {
                this.PublishFatal(ex);
            }
            return false;
        }
        protected virtual bool FlushDocument()
        {
            try
            {
                var buffer = new byte[Stream.Length];

                Stream.Position = 0;
                Stream.Read(buffer, 0, buffer.Length);

                Model.OutputData = buffer;
                return true;
            }
            catch (Exception ex)
            {
                this.PublishFatal(ex);
            }
            return false;
        }
        #endregion protected

        #region Create

        protected virtual void FillPackage(SpreadsheetDocument package)
        {
            var workBookPart = package.AddWorkbookPart();
            var book = new Workbook();
            workBookPart.Workbook = book;

            var stylesPart = workBookPart.AddNewPart<WorkbookStylesPart>();
            stylesPart.Stylesheet = StyleSheetGenerator.CreateStyleSheet();

            CreateSheet(book);
        }

        protected virtual void CreateSheet(Workbook book)
        {
            var part = book.WorkbookPart.AddNewPart<WorksheetPart>();
            var sheetData = new SheetData();
            part.Worksheet = new Worksheet(sheetData);

            var sheet = new Sheet
            {
                Id = book.WorkbookPart.GetIdOfPart(part),
                SheetId = 1,
                Name = "Export"
            };
            var sheets = book.AppendChild<Sheets>(new Sheets());
            sheets.Append(sheet);

            CreateTable(sheetData);
        }
        protected virtual void CreateTable(SheetData sheetData)
        {
            var index = 0;

            sheetData.Append(CreateHeader(++index));
            sheetData.Append(CreateKeys(++index));

            foreach (var entity in Model.Entities)
            {
                sheetData.Append(CreateRow(entity, ++index));
            }
        }
        protected virtual Row CreateHeader(int index)
        {
            var row = new Row
            {
                RowIndex = (uint)index,
            };

            foreach (var exCell in Model.Entities.First())
            {
                row.Append(CreateCell(exCell.Localized, ExCellType.String));
            }

            return row;
        }
        protected virtual Row CreateKeys(int index)
        {
            var row = new Row
            {
                RowIndex = (uint)index,
                Hidden = true,
            };

            foreach (var exCell in Model.Entities.First())
            {
                row.Append(CreateCell(exCell.Name, ExCellType.String));
            }
            return row;
        }
        protected virtual Row CreateRow(IEnumerable<IExCell> exCells, int index)
        {
            var row = new Row { RowIndex = (uint)index };
            foreach (var exCell in exCells)
            {
                row.Append(CreateCell(exCell.Value, exCell.Type));
            }
            return row;
        }
        protected virtual Cell CreateCell(object value, ExCellType exType)
        {
            var cellValue = value;
            var val = value as ValueType;
            var dt = value as DateTime?;

            if (dt.HasValue)
            {
                cellValue = dt.Value.ToOADate();
                //cellValue = dt.Value.ToString("s");
            }
            else if (val.Is())
            {
                cellValue = val.ToDisplay(formatProvider: CultureInfo.InvariantCulture);
            }

            var cell = new Cell
            {
                CellValue = new CellValue((cellValue ?? string.Empty).ToString()),
                DataType = CellValues.Number,

            };

            if (exType.Equals(ExCellType.DateTime))
            {
                //cell.DataType = CellValues.Date;
                cell.StyleIndex = 1;
            }
            else if (exType.Equals(ExCellType.Guid)
                  || exType.Equals(ExCellType.String))
            {
                cell.DataType = CellValues.String;
            }
            else if (exType.Equals(ExCellType.Boolean))
            {
                var b = value as bool?;
                if (b.HasValue)
                {
                    cell.CellValue = new CellValue(b.Value ? "1" : "0");
                }
                cell.DataType = CellValues.Boolean;
            }
            return cell;
        }
        #endregion Create

        #region properties
        protected virtual IExcelExporterData Model { get; set; }
        protected virtual Stream Stream { get; set; }

        StyleSheetGenerator _styleSheetGenerator;
        protected virtual StyleSheetGenerator StyleSheetGenerator
        {
            get { return _styleSheetGenerator ?? (_styleSheetGenerator = new StyleSheetGenerator()); }
        }
        #endregion properties
    }
}